/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package com.xiashitech.agent.instrumentation.dubbo.inst.lt27;


import com.alibaba.dubbo.remoting.exchange.Request;
import com.alibaba.dubbo.remoting.exchange.Response;
import com.xiashitech.agent.cache.status.CacheStatus;
import com.xiashitech.agent.dto.UserRequest;
import com.xiashitech.agent.instrumentation.bridge.Java8Bridge;
import com.xiashitech.agent.instrumentation.dubbo.XSDubboLt27Handler;
import com.xiashitech.agent.instrumentation.helper.calldepth.XSDubboLt27AbstractPeerSendCallDepth;
import com.xiashitech.agent.instrumentation.utils.BaggageUtil;
import com.xiashitech.agent.utils.DurationPrinter;
import com.xiashitech.agent.utils.JsonUtil;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

import java.lang.reflect.InvocationTargetException;
import java.util.Optional;

import static com.xiashitech.agent.instrumentation.dubbo.XSDubboSingletons.dubboInstrumenter;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.*;

public class AbstractPeerSendInstrumentation implements TypeInstrumentation {
   // private static final Logger logger = Logger.getLogger(AbstractPeerSendInstrumentation.class.getName());

    @Override
    public ElementMatcher<TypeDescription> typeMatcher() {
        // return implementsInterface(named("com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelOutboundInvoker"));
        return named("com.alibaba.dubbo.remoting.transport.AbstractPeer");
    }


    @Override
    public void transform(TypeTransformer transformer) {
        transformer.applyAdviceToMethod(
                isMethod()
                        .and(named("send"))
                        /* .and(takesArgument(0, named("java.lang.Object")))
                         .and(takesArguments(1))*/
                        .and(takesArgument(0, named("java.lang.Object")))
                        .and(takesArguments(1))
                        .and(isPublic()),
                AbstractPeerSendInstrumentation.class.getName() + "$SendAdvice");
    }

    @SuppressWarnings("unused")
    public static class SendAdvice {

        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void onEnter(
                @Advice.Argument(value = 0, readOnly = false) Object response,
                @Advice.This Object servletInvocableHandlerMethod,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest
        ) throws Exception {
            DurationPrinter durationPrinter = new DurationPrinter("AbstractPeerSendInstrumentation");
            System.out.println("###mmm3");
            try {
                if (CacheStatus.cannotRun()) {
                    System.out.println("###Cache not run  excute ");
                    return;
                }
                callDepth = CallDepth.forClass(XSDubboLt27AbstractPeerSendCallDepth.class);
                if (callDepth.getAndIncrement() > 0) {
                    System.out.println("###callDepth003  excute "+callDepth.decrementAndGet());
                    return;
                }
                if (response == null) {
                    return;
                }
        /*       Optional<UserRequest> userRequestOptional = Java8Bridge.executeRuleForUserInfoMatchRule(response);
                if (userRequestOptional.isPresent()) {
                    userRequest = userRequestOptional.get();
                } else {
                    userRequest = UserRequest.builder().build();
                }*/
                userRequest = UserRequest.builder().build();
                userRequest.setAdviceObject(servletInvocableHandlerMethod);
                Context parentContext = currentContext();
               if (dubboInstrumenter().shouldStart(parentContext, userRequest)) {
                   parentContext = BaggageUtil.syncBaggage().build().storeInContext(parentContext);
                   context = dubboInstrumenter().start(parentContext, userRequest);
                    if (response instanceof Response) {
                        System.out.println("###send001 response start string :" + response.toString());
                        response = XSDubboLt27Handler.handle(userRequest, response);
                        System.out.println("###send002  response hanle finished string:" + response.toString());
                    } else if (response instanceof Request) {
                        Optional initOptional = XSDubboLt27Handler.init(userRequest, response);
                        if (!initOptional.isPresent()) {
                            System.out.println("###send003  resonse change to request init finished :" + response.toString());
                            System.out.println("###send003  resonse change to request init finished :" + JsonUtil.convertObjectToString(response));
                            return;
                        }

                    } else {
                        System.out.println("###sendd005 response: unknow request");
                    }
                   context = BaggageUtil.syncBaggage().build().storeInContext(context);
                   scope = context.makeCurrent();
               }

            } catch (Throwable e) {
                e.printStackTrace();
            }
            System.out.println("###mmm4");
            //logger.log(XSLevel.DEBUG,"###debug 008");
            durationPrinter.printElapseDuration("SendAdvice b");
        }

        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static void onExit(
                @Advice.Argument(value = 0) Object response,
                @Advice.Thrown Throwable throwable,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest

        ) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
            DurationPrinter durationPrinter = new DurationPrinter("AbstractPeerSendInstrumentation");

            try {
                if (CacheStatus.cannotRun()) {
                    System.out.println("###Cache not Run 006");

                    return;
                }
                if (callDepth.decrementAndGet() > 0) {
                    System.out.println("###callDepth004  excute"+callDepth.decrementAndGet());
                    return;
                }
                if (scope == null) {
                    System.out.println("###mmm5-0");
                    return;
                }
                scope.close();
                Java8Bridge.currentForTraceContextVars()
                        .set("xs.api.responseParams",  JsonUtil.convertObjectToString(response));
                dubboInstrumenter().end(context, userRequest, null, throwable);
                System.out.println("###response exit :" + response.toString());
                System.out.println("###mmm5");
            } catch (Throwable e) {
                e.printStackTrace();
            } finally {
                System.out.println("###Dubbo scope"+scope.toString());
               // ReentryContextStorage.INSTANCE.close();
                System.out.println("###Dubbo OnMethodExit builder excute response "+JsonUtil.convertObjectToString(response));
                //logger.log(XSLevel.DEBUG,"###debug 009");
            }
            durationPrinter.printElapseDuration("SendAdvice e");
        }
    }
}

